﻿using System;

namespace UtilZ.Dotnet.Ex.Normalization
{
    /// <summary>
    /// 归一化器选项参数
    /// </summary>
    public class NormalizerOptions
    {
        /// <summary>
        /// 归一化输入数据最小值
        /// </summary>
        public double? InputDataMin { get; private set; } = null;

        /// <summary>
        /// 归一化输入数据最大值
        /// </summary>
        public double? InputDataMax { get; private set; } = null;

        /// <summary>
        /// 使用映射,仅当输入数据为整形数据且确定范围以及范围在int.MaxValue时才生效
        /// </summary>
        public bool UseMap { get; private set; } = false;

        /// <summary>
        /// 归一化输出结果数据最小值
        /// </summary>
        public double OutputDataMin { get; private set; }

        /// <summary>
        /// 归一化输出结果数据最大值
        /// </summary>
        public double OutputDataMax { get; private set; }

        /// <summary>
        /// 多帧之间的平滑帧数.未指定输入数据范围时有效.
        /// 大于0时为保留最近的平滑帧数内的最小最大值做归一化;
        /// 等于0表示不作平滑,根据每帧中的最大最小值做归一化;
        /// 小于表示取历史上最大最小值做归一化
        /// </summary>
        public int SlidFrames { get; set; } = 0;

        /// <summary>
        /// 获取或设置判断结对值为0的精度,小于此值时认为是0
        /// </summary>
        public double Pre { get; set; } = 0.0001d;


        /// <summary>
        /// 构造函数(适用于确定输入值范围,且范围不算大的场景,否则内存会很大.优点是速度快,缺点是较实时计算有一定的误差)
        /// </summary>
        /// <param name="inputDataMin">归一化输入数据最小值</param>
        /// <param name="inputDataMax">归一化输入数据最大值</param>
        /// <param name="useMap">仅当输入数据为整形数据且确定范围以及范围在int.MaxValue时才生效</param>
        /// <param name="outputDataMin">归一化输出结果数据最小值</param>
        /// <param name="outputDataMax">归一化输出结果数据最大值</param>
        public NormalizerOptions(long inputDataMin, long inputDataMax, bool useMap, double outputDataMin, double outputDataMax)
        {
            if (inputDataMin > inputDataMax)
            {
                throw new ArgumentException("输入数据最小值大于了最大值.");
            }

            if (outputDataMin > outputDataMax)
            {
                throw new ArgumentException("输出结果数据最小值大于了最大值.");
            }

            this.InputDataMin = inputDataMin;
            this.InputDataMax = inputDataMax;
            this.UseMap = useMap;
            this.OutputDataMin = outputDataMin;
            this.OutputDataMax = outputDataMax;
        }

        /// <summary>
        /// 构造函数(适用于不确定输入值范围的场景.优点是误差小,缺点是需要实时计算,速度较映射法会慢一些)
        /// </summary>
        /// <param name="inputDataMin">归一化输入数据最小值</param>
        /// <param name="inputDataMax">归一化输入数据最大值</param>
        /// <param name="outputDataMin">归一化输出结果数据最小值</param>
        /// <param name="outputDataMax">归一化输出结果数据最大值</param>
        public NormalizerOptions(double inputDataMin, double inputDataMax, double outputDataMin, double outputDataMax)
        {
            if (inputDataMin > inputDataMax)
            {
                throw new ArgumentException("输入数据最小值大于了最大值.");
            }

            if (outputDataMin > outputDataMax)
            {
                throw new ArgumentException("输出结果数据最小值大于了最大值.");
            }

            this.InputDataMin = inputDataMin;
            this.InputDataMax = inputDataMax;
            this.OutputDataMin = outputDataMin;
            this.OutputDataMax = outputDataMax;
        }


        /// <summary>
        /// 构造函数(适用于不确定输入值范围的场景.优点是误差小,缺点是需要实时计算,速度较映射法会慢一些)
        /// </summary>
        /// <param name="outputDataMin">归一化输出结果数据最小值</param>
        /// <param name="outputDataMax">归一化输出结果数据最大值</param>
        public NormalizerOptions(double outputDataMin, double outputDataMax)
        {
            if (outputDataMin > outputDataMax)
            {
                throw new ArgumentException("输出结果数据最小值大于了最大值.");
            }

            this.OutputDataMin = outputDataMin;
            this.OutputDataMax = outputDataMax;
        }

        internal bool HasInputRange()
        {
            return this.InputDataMin.HasValue && this.InputDataMax.HasValue;
        }
    }
}
